package com.miniboy.imageloader.util;

import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.util.Log;
import android.widget.ImageView;

import com.miniboy.imageloader.domain.LoadResult;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 作者 ${郭鹏飞}.<br/>
 */
public class HttpUtil {

    private static final String TAG = HttpUtil.class.getSimpleName();

    private static final int CPU_COUNT = Runtime.getRuntime().
            availableProcessors();
    /**
     * 指定默认的核心线程数 默认值为当前的CPU的个数+1
     */
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    /**
     * 指定线程池最大的线程数 默认值为当前的CPU2倍+1
     */
    private static final int MAX_POOL_SIZE = CPU_COUNT * 2 + 1;
    /**
     * 指定非核心线程闲置时超时时长
     */
    private static final long KEEP_ALIVE = 10L;
    /**
     * 指定时间单位
     */
    private static final TimeUnit UNIT_SECONDS = TimeUnit.SECONDS;
    /**
     * 任务队列 存放通过线程池的execute方法提交的Runable
     */
    private static final LinkedBlockingQueue<Runnable> DEFAULT_TASK_QUEUE =
            new LinkedBlockingQueue<>();
    /**
     * 线程工厂 为线程池提供创建新线程的功能 是一个接口
     */
    private static final ThreadFactory DEFAULT_THREAD_FACTORY = new ThreadFactory() {

        /**通过CAS无锁 实现了数据安全*/
        private AtomicInteger mCount = new AtomicInteger();

        @Override
        public Thread newThread(@NonNull Runnable r) {
            return new Thread(r, "HttpUtil Thread " + mCount.getAndIncrement());
        }
    };

    /**
     * 线程池
     */
    private static final Executor DEFAULT_THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE, UNIT_SECONDS,
            DEFAULT_TASK_QUEUE,
            DEFAULT_THREAD_FACTORY
    );

    private static HttpUtil instance = null;

    /**
     * 状态码 成功
     */
    public static final int SUCCESSED = 0x0;
    /**
     * 状态码 失败
     */
    public static final int FAILED = 0x1;
    /**
     * 成功响应
     */
    public static final int RESPONSE_OK = 0x1000;
    /**
     * 状态码 超时
     */
    public static final int ERROR_TIME_OUT = 0x1001;
    /**
     * 状态码 网络响应失败
     */
    public static final int ERROR_RESPONSE_FAILED = 0x1002;
    /**
     * 状态码 没有获取到的网络输入流
     */
    public static final int ERROR_NO_INPUTSTREAM = 0x1003;
    /**
     * 状态码 其他
     */
    public static final int ERRPR_OTHER = 0x1004;

    private HttpUtil() {
    }

    public static HttpUtil getInstance() {
        if (instance == null) {
            synchronized (HttpUtil.class) {
                if (instance == null) {
                    instance = new HttpUtil();
                }
            }
        }
        return instance;
    }

    public synchronized void execute(DownImageTask task, AsyncDownImageHandler handler) {
        execute(task, DEFAULT_THREAD_POOL_EXECUTOR, handler);
    }

    public synchronized void execute(DownImageTask task, Executor executor,
                                     AsyncDownImageHandler handler) {
        if (task == null) {
            throw new NullPointerException("DownImageTask 为空");
        }
        if (executor == null) {
            throw new NullPointerException("Executor 为空");
        }
        imageHandler = handler;
        DEFAULT_THREAD_POOL_EXECUTOR.execute(task);
    }

    private static AsyncDownImageHandler imageHandler;

    private static final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            LoadResult loadResult = (LoadResult) msg.obj;
            if (loadResult == null) {
                return;
            }
            switch (msg.what) {
                case SUCCESSED:
                    if (imageHandler != null) {
                        imageHandler.successed(loadResult);
                    }
                    break;
                case FAILED:
                    if (imageHandler != null) {
                        imageHandler.failed(loadResult.getStatusCode(),
                                loadResult.getImageView(), loadResult.getUrl());
                    }
                    break;
                default:
                    return;
            }
        }
    };

    public static class DownImageTask implements Runnable {

        /**
         * 图片下载地址
         */
        private String mPath;
        /**
         * 图片显示指定的宽
         */
        private int mWidth;
        /**
         * 图片显示指定的高
         */
        private int mHeight;
        /**
         * 显示图片ImageView
         */
        private ImageView mImageView;

        public DownImageTask(String path, int width, int height,
                             ImageView imageView) {
            mPath = path;
            mWidth = width;
            mHeight = height;
            mImageView = imageView;
        }

        @Override
        public void run() {
            HttpURLConnection httpURLConnection = null;
            InputStream is = null;
            try {
                URL url = new URL(mPath);
                httpURLConnection = (HttpURLConnection) url.openConnection();
                // 设置读取数据超时时间 单位毫秒
                httpURLConnection.setReadTimeout(10 * 1000);
                // 设置连接超时时间
                httpURLConnection.setConnectTimeout(10 * 1000);
                // 设置请求方式
                httpURLConnection.setRequestMethod("GET");
                //  URL 连接进行输入
                httpURLConnection.setDoInput(true);
                // 注意这个地方 GET 请求不需要这个设置
                // 因为参数是直接拼接在地址里面 不需要
                //  URL 连接进行输出
                httpURLConnection.setDoOutput(true);
                // 连接
                httpURLConnection.connect();
                // 判断请求的相应码
                if (HttpURLConnection.HTTP_OK == httpURLConnection.getResponseCode()) {
                    is = httpURLConnection.getInputStream();
                    if (is == null) {
                        sendMessage(FAILED, ERROR_NO_INPUTSTREAM, mPath, mImageView, null);
                    } else {
                        Bitmap bitmap = BitmapUtil.decodeSampleBitmap(is, mWidth, mHeight);
                        sendMessage(SUCCESSED, RESPONSE_OK, mPath, mImageView, bitmap);
                    }
                } else {
                    Log.w(TAG, ">>>>>> " + httpURLConnection.getResponseCode() + " <<<<<<");
                    sendMessage(FAILED, ERROR_RESPONSE_FAILED, mPath, mImageView, null);
                }
            } catch (SocketTimeoutException e) {
                sendMessage(FAILED, ERROR_TIME_OUT, mPath, mImageView, null);
            } catch (Exception e) {
                sendMessage(FAILED, ERRPR_OTHER, mPath, mImageView, null);
            } finally {
                if (httpURLConnection != null) {
                    httpURLConnection.disconnect();
                }
                if (is != null) {
                    try {
                        is.close();
                        is = null;
                    } catch (IOException e) {
                        e.printStackTrace();
                        is = null;
                    }
                }
            }
        }

        private void sendMessage(int type, int statusCode, String url,
                                 ImageView imageView, Bitmap bitmap) {
            Message msg = mHandler.obtainMessage(type);
            LoadResult result = new LoadResult();
            result.setStatusCode(statusCode);
            result.setImageView(imageView);
            result.setUrl(url);
            result.setBitmap(bitmap);
            msg.obj = result;
            mHandler.sendMessage(msg);
        }
    }

    public interface AsyncDownImageHandler {
        /**
         * 异步成功回调
         *
         * @param loadResult {@link LoadResult}
         */
        void successed(LoadResult loadResult);

        /**
         * 异步失败回调
         *
         * @param errorCode 失败状态码
         * @param imageView ImageView
         * @param url       下载地址
         */
        void failed(int errorCode, ImageView imageView, String url);
    }

}
